/*************************************************************************
	> File Name: BlockQueue.hpp
	> Author: H.M. Lau
	> Mail: hm.lau@outlook.com
	> Created Time: 2023/09/26 10:19
 ************************************************************************/

#ifndef _BLOCKQUEUE_H
#define _BLOCKQUEUE_H

#include "comm.hpp"

const int gDefaultCap = 5;

template <class T>
class BlockQueue
{
public:
    BlockQueue (int capacity = gDefaultCap) : _capacity(capacity)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_Empty, nullptr);
        pthread_cond_init(&_Full, nullptr);
    }

    virtual ~BlockQueue ()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_Empty);
        pthread_cond_destroy(&_Full);
    }

    void push(const T &in)
    {
        pthread_mutex_lock(&_mtx);
        while (isFull()) {
            pthread_cond_wait(&_Full, &_mtx);
        }
        _bq.push(in);
        pthread_cond_signal(&_Empty);
        pthread_mutex_unlock(&_mtx);
    }

    void pop(T *out)
    {
        pthread_mutex_lock(&_mtx);
        while (isEmpty()) {
            pthread_cond_wait(&_Empty, &_mtx);
        }
        *out = _bq.front();
        _bq.pop();
        pthread_cond_signal(&_Full);
        pthread_mutex_unlock(&_mtx);
    }

private:
    std::queue<T> _bq;
    int _capacity;
    pthread_mutex_t _mtx;
    pthread_cond_t _Empty;
    pthread_cond_t _Full;

    bool isEmpty()
    {
        return _bq.empty();
    }

    bool isFull()
    {
        return _bq.size() == gDefaultCap;
    }
};
#endif
